package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author mgx
 * @version 1.0
 * @description: 网关-用户认证
 * @date 2022-12-02 10:50
 */
@Component
@SuppressWarnings("all")
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    // springboot提供的路径匹配器
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Value("${authUrls.url}")
    private String authUrls;

    /**
     * 过滤接口
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        // 获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        // 获取资源相应的路由信息
        String path = request.getURI().getPath();
        // 1. 内部接口 -- 拒绝访问
        if(antPathMatcher.match("/api/**/inner/**", path)) {
            return out(response, ResultCodeEnum.PERMISSION);
        }

        // 获取用户id
        String userId = this.getUserId(request);
        // System.out.println("userId = " + userId);

        // 判断是不是ip被盗
        if("-1".equals(userId)) {
            return out(response, ResultCodeEnum.ILLEGAL_REQUEST);
        }

        // 2. 认证接口
        if(antPathMatcher.match("/api/**/auth/**", path)) {
            // 判断userId是否为空 -- 是否登录
            if(StringUtils.isEmpty(userId)) {
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        // 3. 认证白名单
        if(!StringUtils.isEmpty(authUrls)) {
            // 分割一下
            String[] split = authUrls.split(",");
            if(split != null && split.length > 0) {
                for (String url : split) {
                    // 访问的是白名单的页面并且没有登录
                    if(path.indexOf(url) != -1 && StringUtils.isEmpty(userId)) {
                        // 重定向到登录页面
                        // 设置状态码
                        response.setStatusCode(HttpStatus.SEE_OTHER);
                        response.getHeaders().set(HttpHeaders.LOCATION,"http://www.gmall.com/login.html?originUrl="+request.getURI());

                        // 放行去登录页面
                        return response.setComplete();
                    }
                }
            }
        }
        // 获取userTempId
        String userTempId = this.getUserTempId(request);

        // 把userId获取userTempId存储到request中进行转发给其他服务器
        if(!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)) {
            if(!StringUtils.isEmpty(userId)) {
                request.mutate().header("userId", userId).build();
            }
            if(!StringUtils.isEmpty(userTempId)) {
                request.mutate().header("userTempId", userTempId).build();
            }
            // 讲现在的request变成我们的exchange
            return chain.filter(exchange.mutate().request(request).build());
        }

        // 放行接口
        return chain.filter(exchange);
    }

    /**
     * 获取临时用户id
     * @param request
     * @return
     */
    private String getUserTempId(ServerHttpRequest request) {
        String userTempId = "";
        // 先从请求头中获取
        List<String> list = request.getHeaders().get("userTempId");
        if(!CollectionUtils.isEmpty(list)) {
            userTempId = list.get(0);
        }
        // 如果请求头没获取到，就请求
        if(StringUtils.isEmpty(userTempId)) {
            MultiValueMap<String, HttpCookie> cookieMultiValueMap =  request.getCookies();
            HttpCookie cookie = cookieMultiValueMap.getFirst("userTempId");
            if(cookie != null){
                userTempId = URLDecoder.decode(cookie.getValue());
            }

        }
        return userTempId;
    }


    /**
     * 获取用户id:
     * 1. 存在与token匹配的userId 返回
     * 2. ip被盗用，即ip不匹配返回-1
     * 3. 不存在该userId 返回""
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        String token = null;
        // 从请求头中获取token
        List<String> tokenList = request.getHeaders().get("token");
        if(!CollectionUtils.isEmpty(tokenList)) {
            token = tokenList.get(0);
        }
        // 对此时的token中是否有值判断一下
        if(StringUtils.isEmpty(token)) {
            // 如果请求头中没有 -- 从cookie中获取token
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            HttpCookie cookie = cookies.getFirst("token");
            if(cookie != null) {
                token = cookie.getValue();
            }
        }
        // 判断此时token是否存在
        if(!StringUtils.isEmpty(token)) {
            // 从redis中获取信息
            String strJson = (String) redisTemplate.opsForValue().get("user:login:" + token);
            // 当时存储redis的时候是对象转化为string存储的，所以取出可以转化成对象
            JSONObject jsonObject = JSONObject.parseObject(strJson);
            // 获取ip
            String ip = jsonObject.getString("ip");
            // 获取当前ip
            String nowIp = IpUtil.getGatwayIpAddress(request);
            // 判断是不是相同
            if (!ip.equals(nowIp)) {
                // 不相同，返回-1
                return "-1";
            }
            // 出来判断相当于是已经通过了
            // 拿到userId返回即可
            return jsonObject.getString("userId");
        }

        // 不存在用户id
        return "";
    }

    /**
     * 设置响应结果输出信息
     * @param response
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        // 返回用户没有权限登录
        Result result = Result.build(null, resultCodeEnum);
        // 转化成字节数组
        byte[] bytes = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        // 数据缓冲DataBuffer
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        // 防止中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(wrap));
    }
}
